/*
 * ============================================================================
 *
 *       Filename:  sockarr.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2014年09月02日 14时27分53秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jianxi sun (jianxi), ycsunjane@gmail.com
 *   Organization:  
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>

/* epoll */
#include <sys/epoll.h>

#include "common.h"
#include "thread.h"
#include "dllayer.h"
#include "netlayer.h"
#include "sockarr.h"
#include "list.h"

static int epl;
LIST_HEAD(sockarr_head);
static INIT_LOCK(sockarr_lock);

static void epoll_rearm(struct sockarr_t *__sock)
{
	int ret;

	__sock->ev.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
	ret = epoll_ctl(epl, EPOLL_CTL_MOD, 
		__sock->sock, &__sock->ev);
	if(ret < 0) {
		sys_err("Rearm epoll fd failed: %s(%d)\n", 
			strerror(errno), errno);
		exit(-1);
	}
}

static void epoll_free(int sock)
{
	if(sock < 0) return;

	int ret;
	/* del in epoll */
	ret = epoll_ctl(epl, EPOLL_CTL_DEL, sock, NULL);
	if(ret < 0) {
		sys_err("Del epoll fd failed: %s(%d)\n", 
			strerror(errno), errno);
		exit(-1);
	}
	close(sock);
}

void debug_event(uint32_t event)
{
	if(!debug) return;
	sys_info("=================event: %x===================\n", event);
	if(event & EPOLLIN) sys_info("EPOLLIN ");
	if(event & EPOLLOUT) sys_info("EPOLLOUT ");
	if(event & EPOLLRDHUP) sys_info("EPOLLRDHUP ");
	if(event & EPOLLPRI) sys_info("EPOLLPRI ");
	if(event & EPOLLERR) sys_info("EPOLLERR ");
	if(event & EPOLLHUP) sys_info("EPOLLHUP ");
	if(event & EPOLLET) sys_info("EPOLLET ");
	if(event & EPOLLONESHOT) sys_info("EPOLLONESHOT ");
	sys_info("\n");
	sys_info("=================event: %x===================\n", event);
}

#define EPOLLNUM 	(10)
void * sockarr_recv(void *arg)
{
	int ret, i;
	struct sockarr_t *sockarr;
	static struct epoll_event epollarr[EPOLLNUM];

	while(1) {
		ret = epoll_wait(epl, epollarr, EPOLLNUM, -1);
		if(ret < 0) {
			if(errno == EINTR)
				continue;
			sys_err("Epoll wait failed: %s(%d)\n", 
				strerror(errno), errno);
			exit(-1);
		}

		for(i = 0; i < ret; i++) {
			sockarr = epollarr[i].data.ptr;
			sockarr->bakevent = epollarr[i].events;

			if(sockarr->func(sockarr) == 0)
				epoll_rearm(sockarr);
		}
	}
	return NULL;	
}

int sockarr_send(int proto, int sock, 
	unsigned char *dmac, char *msg, int size)
{
	pr_pkt(msg, size);
	char macstr[MACSTR_LEN]; 
	switch(proto) {
	case MSG_PROTO_ETH:
		sys_debug("Send packet through datalink layer:"
			"dmac: %s, msg: %p, size: %d\n",
			mac_to_macstr(dmac, macstr), msg, size);
		if(size > DLL_PKT_DATALEN) {
			sys_err("dll max packet len: %lu\n", 
				DLL_PKT_DATALEN);
			exit(-1);
		}

		return dll_sendpkt(dmac, msg, size);
	case MSG_PROTO_TCP:
		sys_debug("Send packet through tcp layer:"
			"sock: %d, msg: %p, size: %d\n", 
			sock, msg, size);

		struct nettcp_t net;
		net.sock = sock;
		return tcp_sendpkt(&net, msg, size);
	default:
		sys_err("Invalid protocol\n");
		return -1;
	}
	return -1;
}

void sockarr_init()
{
	epl = epoll_create1(EPOLL_CLOEXEC);
	if(epl < 0) {
		sys_err("Create epoll failed: %s(%d)\n", 
			strerror(epl), epl);
		exit(-1);
	}
}

struct sockarr_t * 
sockarr_insert(int sock, int (*func) (void *), void *arg)
{
	int ret;
	struct sockarr_t *__sock;

	__sock = safe_malloc(sizeof(struct sockarr_t));
	__sock->ev.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
	__sock->ev.data.ptr = __sock;
	__sock->sock = sock;
	__sock->func = func;
	__sock->arg = arg;

	/* add in epoll */
	ret = epoll_ctl(epl, EPOLL_CTL_ADD, 
		__sock->sock, &__sock->ev);
	if(ret < 0) {
		sys_err("Add epoll fd failed: %s(%d)\n", 
			strerror(errno), errno);
		exit(-1);
	}

	LOCK(&sockarr_lock);
	list_add_tail(&__sock->list, &sockarr_head);
	UNLOCK(&sockarr_lock);
	return __sock;
}

int sockarr_delete(struct sockarr_t *arr, int sock)
{
	struct sockarr_t *pos;
	LOCK(&sockarr_lock);
	if(!arr) {
		list_for_each_entry(pos, &sockarr_head, list) {
			if(sock == pos->sock) {
				arr = pos;
				break;
			}
		}
	}
	if(!arr) return -1;

	list_del(&arr->list);
	epoll_free(arr->sock);
	UNLOCK(&sockarr_lock);
	free(arr);
	return 0;
}

void sockarr_clr()
{
	struct sockarr_t *pos, *n;
	LOCK(&sockarr_lock);
	list_for_each_entry_safe(pos, n, &sockarr_head, list) {
		list_del(&pos->list);
		epoll_free(pos->sock);
		free(pos);
	}
	UNLOCK(&sockarr_lock);
}

